//装饰模式
//    亦称： 装饰者模式、 装饰器模式、 Wrapper、 Decorator
//1.意图
//装饰器是一种结构设计模式，通过将这些对象放置在包含行为的特殊包装对象中，可以使它们附加新的行为。

//2.问题
//想象一下，您正在使用一个通知库，该库可让其他程序将重要事件通知其用户。
//
//该库的初始版本基于Notifier类，该类只有几个字段，一个构造函数和一个send方法。该方法可以从客户端接受消息参数，并将消息发送到通过其构造函数传递给通知程序的电子邮件列表。充当客户端的第三方应用程序应该一次创建和配置通知程序对象，然后在发生重要事件时使用它。

//在某个时候，您意识到图书馆的用户不仅期望电子邮件通知。他们中的许多人希望收到有关关键问题的SMS。其他人希望在Facebook上收到通知，当然，企业用户希望获得Slack通知。

//那有多难？您扩展了Notifier类，并将其他通知方法放入新的子类中。现在，客户端应该实例化所需的通知类，并将其用于所有其他通知。
//
//但是后来有人合理地问你：“为什么不能一次使用几种通知类型？如果您的房屋着火了，您可能希望通过每个渠道得到通知。”
//
//您试图通过创建特殊的子类来解决该问题，这些子类将多个通知方法组合在一个类中。但是，很快就很明显，这种方法将极大地膨胀代码，不仅是库代码，而且还有客户代码。

//您必须找到其他构造通知类的方法，以使通知类的数量不会意外地打破吉尼斯纪录。

//3.解决
//当您需要更改对象的行为时，首先想到的是扩展类。但是，继承有几个需要注意的严重警告。
//
//继承是静态的。您无法在运行时更改现有对象的行为。您只能将整个对象替换为从其他子类创建的另一个对象。
//子类只能有一个父类。在大多数语言中，继承不能使一个类同时继承多个类的行为。
//克服这些警告的方法之一是使用聚合或组合而不是继承。两种选择的工作方式几乎相同：一个对象引用另一个对象，并将其委派给工作，而通过继承，对象本身能够执行该工作，并从其超类继承行为。
//
//使用这种新方法，您可以轻松地将链接的“帮助程序”对象替换为另一个对象，从而在运行时更改容器的行为。一个对象可以使用各种类的行为，可以引用多个对象并将它们委派给所有工作。聚合/组成是包括Decorator在内的许多设计模式背后的关键原则。关于这一点，让我们回到模式讨论。

//包装器是Decorator模式的替代昵称，清楚地表达了该模式的主要思想。 “包装器”是可以与某个“目标”对象链接的对象。包装器包含与目标相同的方法集，并将其接收的所有请求委托给它。但是，包装程序可以通过在将请求传递给目标之前或之后执行某些操作来更改结果。
//
//什么时候一个简单的包装器成为真正的装饰器？如前所述，包装器实现了与包装对象相同的接口。因此，从客户的角度来看，这些对象是相同的。使包装器的引用字段接受该接口之后的任何对象。这将使您可以用多个包装器覆盖一个对象，并向其中添加所有包装器的组合行为。
//
//在我们的通知示例中，让我们将简单的电子邮件通知行为保留在Notifier基类中，但将所有其他通知方法都变为装饰器。



#include <iostream>

/**
 * The base Component interface defines operations that can be altered by
 * decorators.
 */
class Component {
public:
  virtual ~Component() {}
  virtual std::string Operation() const = 0;
};
/**
 * Concrete Components provide default implementations of the operations. There
 * might be several variations of these classes.
 */
class ConcreteComponent : public Component {
public:
  std::string Operation() const override {
    return "ConcreteComponent";
  }
};
/**
 * The base Decorator class follows the same interface as the other components.
 * The primary purpose of this class is to define the wrapping interface for all
 * concrete decorators. The default implementation of the wrapping code might
 * include a field for storing a wrapped component and the means to initialize
 * it.
 */
class Decorator : public Component {
  /**
   * @var Component
   */
protected:
  Component* component_;

public:
  Decorator(Component* component) : component_(component) {
  }
  /**
   * The Decorator delegates all work to the wrapped component.
   */
  std::string Operation() const override {
    return this->component_->Operation();
  }
};
/**
 * Concrete Decorators call the wrapped object and alter its result in some way.
 */
class ConcreteDecoratorA : public Decorator {
  /**
   * Decorators may call parent implementation of the operation, instead of
   * calling the wrapped object directly. This approach simplifies extension of
   * decorator classes.
   */
public:
  ConcreteDecoratorA(Component* component) : Decorator(component) {
  }
  std::string Operation() const override {
    return "ConcreteDecoratorA(" + Decorator::Operation() + ")";
  }
};
/**
 * Decorators can execute their behavior either before or after the call to a
 * wrapped object.
 */
class ConcreteDecoratorB : public Decorator {
public:
  ConcreteDecoratorB(Component* component) : Decorator(component) {
  }

  std::string Operation() const override {
    return "ConcreteDecoratorB(" + Decorator::Operation() + ")";
  }
};
/**
 * The client code works with all objects using the Component interface. This
 * way it can stay independent of the concrete classes of components it works
 * with.
 */
void ClientCode(Component* component) {
  // ...
  std::cout << "RESULT: " << component->Operation();
  // ...
}

int main() {
  /**
   * This way the client code can support both simple components...
   */
  Component* simple = new ConcreteComponent;
  std::cout << "Client: I've got a simple component:\n";
  ClientCode(simple);
  std::cout << "\n\n";
  /**
   * ...as well as decorated ones.
   *
   * Note how decorators can wrap not only simple components but the other
   * decorators as well.
   */
  Component* decorator1 = new ConcreteDecoratorA(simple);
  Component* decorator2 = new ConcreteDecoratorB(decorator1);
  std::cout << "Client: Now I've got a decorated component:\n";
  ClientCode(decorator2);
  std::cout << "\n";

  delete simple;
  delete decorator1;
  delete decorator2;

  return 0;
}